<?php

namespace app\admin\controller\booking;

use app\admin\model\booking\Calendar;
use app\admin\library\Auth;
use think\Db;
use think\exception\PDOException;
use think\exception\ValidateException;
use app\admin\model\booking\Lodgers;

/**
 * 订单管理
 *
 * @icon fa fa-circle-o
 */
class Order extends Base
{

    /**
     * Order模型对象
     * @var \app\admin\model\booking\Order
     */
    protected $model = null;
    protected $dataLimitField = 'source_id';
    protected $dataLimitFieldAutoFill = false;

    public function _initialize()
    {
        parent::_initialize();
        $this->model = new \app\admin\model\booking\Order;
        $this->view->assign("typeList", $this->model->getTypeList());
        $this->view->assign("statusList", $this->model->getStatusList());
        $this->view->assign("statusGoods", $this->model->getStatusGoods());
    }

    /**
     * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
     * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
     * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
     */

    protected function getDataLimitAdminIds()
    {
        $auth = Auth::instance();
        if ($this->addonsConfig['isStoreAllocate'] == 1 && !$auth->isSuperAdmin()) {
            return $this->authHouseIds;
        }
    }

    /**
     * 房间订单查看
     */
    public function index()
    {
        //当前是否为关联查询
        $this->relationSearch = false;
        //设置过滤方法
        $this->request->filter(['strip_tags', 'trim']);
        if ($this->request->isAjax()) {
            //如果发送的来源是Selectpage，则转发到Selectpage
            if ($this->request->request('keyField')) {
                return $this->selectpage();
            }
            list($where, $sort, $order, $offset, $limit) = $this->buildparams();

            $list = $this->model
                ->with(['User'])
                ->where($where)
                ->where('type', 0)
                ->order($sort, $order)
                ->paginate($limit);

            foreach ($list as $item) {
                if ($item['type'] == 0) {
                    $item->source = $item->source()->field('id,name')->find();
                } elseif ($item['type'] == 1) {
                    $item->source = $item->source()->select();
                }
            }

            $result = array("total" => $list->total(), "rows" => $list->items());

            return json($result);
        }
        return $this->view->fetch();
    }


    /**
     * 商品订单查看
     */
    public function goods()
    {
        //当前是否为关联查询
        $this->relationSearch = false;
        //设置过滤方法
        $this->request->filter(['strip_tags', 'trim']);
        if ($this->request->isAjax()) {
            //如果发送的来源是Selectpage，则转发到Selectpage
            if ($this->request->request('keyField')) {
                return $this->selectpage();
            }
            list($where, $sort, $order, $offset, $limit) = $this->buildparams();

            $list = $this->model
                ->with(['User'])
                ->where($where)
                ->where('type', 1)
                ->order($sort, $order)
                ->paginate($limit);

            foreach ($list as $item) {
                if ($item['type'] == 0) {
                    $item->source = $item->source()->field('id,name')->find();
                } elseif ($item['type'] == 1) {
                    $item->source = $item->source()->select();
                }
            }

            $result = array("total" => $list->total(), "rows" => $list->items());

            return json($result);
        }
        return $this->view->fetch();
    }

    /**
     * 添加
     */
    public function add()
    {
        if ($this->request->isPost()) {
            $params = $this->request->post("row/a");
            if ($params) {
                $params = $this->preExcludeFields($params);
                if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
                    $params[$this->dataLimitField] = $this->auth->id;
                }
                $result = false;
                if (!isset($params['lodgers']) || empty($params['lodgers'])) {
                    $this->error('请录入住客信息！');
                }
                Db::startTrans();
                try {
                    //是否采用模型验证
                    if ($this->modelValidate) {
                        $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
                        $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
                        $this->model->validateFailException(true)->validate($validate);
                    }
                    //添加住客信息
                    $lodgers = (array)json_decode($params['lodgers'], true);
                    list($lodgers_ids, $lodgers_data) = Lodgers::edit($lodgers, [], $params['user_id']);
                    $this->checkedLodger($lodgers_data, $params['checkin_time'], $params['leave_time']);
                    $params['lodgers_ids'] = join(',', $lodgers_ids);
                    //订单id
                    $params['orderid'] = date("Ymdhis") . sprintf("%08d", $params['user_id']) . mt_rand(1000, 9999);
                    //入住人数
                    $params['nums'] = count($lodgers_ids);
                    $result = $this->model->allowField(true)->save($params);
                    Db::commit();
                } catch (ValidateException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (PDOException $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                } catch (\Exception $e) {
                    Db::rollback();
                    $this->error($e->getMessage());
                }
                if ($result !== false) {
                    $this->success();
                } else {
                    $this->error(__('No rows were inserted'));
                }
            }
            $this->error(__('Parameter %s can not be empty', ''));
        }
        //快捷预订参数
        $house_id = $this->request->param('house_id');
        $calendar = $this->request->param('calendar');
        $checkin_time = '';
        $leave_time = '';
        $money = '';
        if (!empty($calendar)) {
            $times = explode(' - ', $calendar);
            $checkin_time = $times[0];
            $leave_time = $times[1] ?? date("Y-m-d", strtotime($checkin_time) + 86400);
            //计算得到金额
            !empty($house_id) && $money = Calendar::totalHousePrice($house_id, $checkin_time, $leave_time);
        }
        $this->assign('money', $money);
        $this->assign('house_id', $house_id);
        $this->assign('checkin_time', $checkin_time);
        $this->assign('leave_time', $leave_time);
        return $this->view->fetch();
    }


    //详情
    public function detail()
    {

        $id = $this->request->param('ids');
        if (!$id) {
            $this->error('参数错误');
        }
        $row = $this->model->with(['User', 'UserCoupon' => ['Coupon']])->where('id', $id)->find();
        if (!$row) {
            $this->error('记录未找到');
        }
        $row->source = $row->source;

        if ($row['outamount'] > 0 && $row['outtype'] == 1) {
            $this->refunded($row);
        }

        $lodgers = Lodgers::where('id', 'IN', $row['lodgers_ids'])->select();
        $this->view->assign('lodgers', json_encode($lodgers, JSON_UNESCAPED_UNICODE));
        $this->view->assign('row', $row);
        return $this->view->fetch();
    }

    //详情
    public function goods_detail()
    {

        $id = $this->request->param('ids');
        if (!$id) {
            $this->error('参数错误');
        }
        $row = $this->model->with(['User', 'UserCoupon' => ['Coupon']])->where('id', $id)->find();
        if (!$row) {
            $this->error('记录未找到');
        }
        $row->source = $row->source()->select();

        if ($row['outamount'] > 0 && $row['outtype'] == 1) {
            $this->refunded($row);
        }
        $row['params'] = (array)json_decode($row['params'], true);

        $this->view->assign('row', $row);
        return $this->view->fetch();
    }

    //单个字段编辑
    public function edit_info()
    {
        $id = $this->request->post('id');
        $field = $this->request->post('field');
        $value = $this->request->post('value');
        $amount = $this->request->post('amount/d');
        //退房参数
        $out_type = $this->request->post('out_type/d'); //1 线下退，2线上退
        $out_money = $this->request->post('out_money'); //退款金额

        if (!$id || !$field || !$value) {
            $this->error('参数错误');
        }
        if ($amount < 0) {
            $this->error('订单金额不能为负数！');
        }
        $row = $this->model->with(['User'])->where('id', $id)->find();
        if (!$row) {
            $this->error('记录未找到');
        }
        if (!isset($row[$field])) {
            $this->error('字段不存在');
        }
        if (!in_array($field, ['status', 'actual_checkin_time', 'actual_checkout_time', 'memo'])) {
            $this->error('没有权限更改！');
        }
        //退房操作，判断是否需要退款
        if ($field == 'status' && $row['type'] == 0 && in_array($value, ['evaluate', 'canceled']) && in_array($row['status'], ['checkouting', 'paid'])) {
            $surplus = (strtotime(date('Y-m-d')) - $row['leave_time']) / 86400; //剩余几天
            if ($surplus < 0) {
                //计算退费
                $total_day = max(0, $row['leave_time'] - $row['checkin_time']) / 86400; //共几天

                $surplus = abs($surplus);
                $surplus = min($surplus, $total_day);

                $outMoney = bcmul(bcdiv($surplus, $total_day, 2), $row['payamount'], 2); //退费
                $outMoney = min($row['payamount'], $outMoney);

                //退款和退房，是否在线退款，线下退款。
                if (!$out_type) {
                    $this->error(
                        '请确定退费金额是否准确！',
                        '',
                        [
                            'surplus'   => $surplus,
                            'out_money' => $outMoney,
                            'paytype'   => $row['paytype'],
                            'total_day' => $total_day
                        ]
                    );
                } elseif ($out_type == 2) {
                    if ($out_money <= 0 || $out_money > $row['payamount']) {
                        $this->error('线上退款金额错误！不能为0或大于支付金额');
                    }
                    //线上退款
                    \app\admin\model\booking\Order::refund($row->orderid, $row->paytype, $out_money);
                    $row->outamount = $out_money;
                    $row->outtype = 1;
                } else {
                    //线下退
                    $row->outamount = $out_money;
                }
            }
        }

        //支付订单取消时，操作退款
        if ($field == 'status' && $value == 'canceled' && $row['status'] == 'refunding') {
            $row->outamount = $row['payamount'];
            $config = get_addon_config('booking');
            if ($config['order_refund_sync']) { //开启了同步退款
                $row->outtype = 1;
            }
        }
        //到店付入住
        if ($row['status'] == 'toshopay') {
            $row->method = 'system';
            $row->paytype = 'system';
            $row->paytime = time();
            $row['payamount'] != $amount && $row->payamount = $amount;
        }
        $row->$field = $value;
        $row->save();
        $this->success('保存成功');
    }


    /**
     * 保持订单住客信息
     */
    public function save_lodgers()
    {
        $lodgers = $this->request->post('lodgers');
        $id = $this->request->post('id');
        if (!$lodgers || !$id) {
            $this->error('参数错误');
        }
        $order = $this->model->get($id);
        if (!$order) {
            $this->error('订单记录未找到');
        }
        $lodgers = (array)json_decode($lodgers, true);
        Db::startTrans();
        try {
            //排除订单存在的
            $exclude_ids = (array)json_decode($order['lodgers_ids'], true);
            list($lodgers_ids, $params) = Lodgers::edit($lodgers, $exclude_ids, $order['user_id']);
            $this->checkedLodger($params, $order['checkin_time'], $order['leave_time']);
            $order->lodgers_ids = join(',', $lodgers_ids);
            $order->save();
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            $this->error($e->getMessage());
        }
        $this->success('保存成功！');
    }

    //校验住客是否重复订房
    private function checkedLodger($lodgers, $start_time, $end_time)
    {
        $startTime = is_numeric($start_time) ? $start_time : strtotime($start_time);
        $endTime = is_numeric($end_time) ? $end_time : strtotime($end_time);
        //校验同一入住时间，是否有预订有房
        foreach ($lodgers as $item) {

            $hasCheckin = $this->model->where("FIND_IN_SET(lodgers_ids,'{$item['id']}')")->where(function ($query) use ($startTime, $endTime) {

                $query->where(function ($q) use ($startTime) {
                    $q->where('checkin_time', '>=', $startTime)->where('leave_time', '<', $startTime);
                })->whereOr(function ($q) use ($endTime) {
                    $q->where('checkin_time', '<', $endTime)->where('leave_time', '>=', $endTime);
                })->whereOr(function ($q) use ($startTime, $endTime) {
                    $q->where('checkin_time', '>=', $startTime)->where('leave_time', '<', $endTime);
                });
            })->where('status', 'IN', ['created', 'paid', 'toshopay', 'checkined'])->count();

            if ($hasCheckin > 0) {
                throw new \Exception("{$item['name']}于" . date('Y-m-d', $startTime) . "至" . date('Y-m-d', $endTime) . "之间已有订单信息，无法重复提交！");
            }
        }
    }

    protected function refunded($order)
    {
        $config = \addons\epay\library\Service::getConfig($order['paytype']);
        $response = null;
        try {
            if ($order['paytype'] == 'wechat') {
                $response = \Yansongda\Pay\Pay::wechat($config)->find([
                    'type'         => in_array($order['method'], ['miniapp', 'app']) ? $order['method'] : '',
                    'out_trade_no' => $order['orderid']
                ], 'refund');
            } elseif ($order['paytype'] == 'alipay') {
                $response = \Yansongda\Pay\Pay::alipay($config)->find([
                    'out_trade_no'   => $order['orderid'],
                    'out_request_no' => $order['orderid']
                ], 'refund');
            }
        } catch (\Exception $e) {
            $this->assign('refunded_info', '查询退款失败，' . $e->getMessage());
        }
        if ($order['paytype'] == 'wechat') {
            if ($response && $response['return_msg'] == 'OK') {
                $this->assign('refunded_info', '已成功退款：' . $response->refund_fee / 100 . '元。');
            }
        } elseif ($order['paytype'] == 'alipay') {
            if ($response && $response['msg'] == 'Success') {
                $this->assign('refunded_info', '已成功退款：' . $response->refund_amount . '元。');
            }
        } else {
            $this->assign('refunded_info', "未知支付类型");
        }
    }
}
